/* HPAudit: Always use strict mode : hp-specs-use-strict-mode : 1 : 0 : issue329.ts */
"use strict";
/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import type GridLayoutItemInfo from '../../../launchercommon/bean/GridLayoutItemInfo';
import { GridOccupyStatusEnum } from './GridOccupyStatus';
import ViewCluster from './ViewCluster';
import { CommonConstants } from '../../../launchercommon/constants/CommonConstants';
import GridOccupyStatus from './GridOccupyStatus';
import ItemConfiguration from './ItemConfiguration';
import List from '@ohos.util.List';
import { RectItem } from '../../utils/Rect';
import Stack from '@ohos.util.Stack';
import CellAndSpan from './CellAndSpan';
import { LogDomain, LogHelper } from '../../../framework/utils/LogHelper';
const TAG = 'CellLayoutDragDelegate';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.HOME, TAG);
export default class CellLayoutDragDelegate {
  private mTempRectStack = new Stack<RectItem>();
  private mPaddingLeft: number;
  private mPaddingTop: number;
  private mRow: number;
  private mColumn: number;
  private mGridItemWidth: number;
  private mGridItemHeight: number;
  private mColumnsGap: number;
  private mRowsGap: number;
  /* HPAudit: Object properties should be initialized : hp-performance-initialize-obj-props : 1 : 41 : issue329.ts */
  private mDirectionVector: number[];
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 42 : issue329.ts */
  private mIntersectingViews: List<any> = new List();
  private mOccupiedRect: RectItem = new RectItem(0, 0, 0, 0);
  /* HPAudit: Object properties should be initialized : hp-performance-initialize-obj-props : 1 : 44 : issue329.ts */
  private reArrangeSolutionSuccess: boolean = false;
  private reNearArrangeSolutionSuccess: boolean = false;
  constructor(paddingLeft: number, paddingTop: number, row: number, column: number, gridItemWidth: number, gridItemHeight: number, rowsGap: number, columnsGap: number) {
    this.mPaddingLeft = paddingLeft;
    this.mPaddingTop = paddingTop;
    this.mRow = row;
    this.mColumn = column;
    this.mGridItemWidth = gridItemWidth;
    this.mGridItemHeight = gridItemHeight;
    this.mRowsGap = rowsGap;
    this.mColumnsGap = columnsGap;
  }

  /**
   *
   * @param x
   * @param y
   * @param isIgnoreOccupied 找寻的可用空位是否包含已经被占用的位置，true代表不能是已经被占用的位置，false代表可以是已
   *     经被占用的位置
   */
  public findNearestArea(cellAndSpan: CellAndSpan): number[] {
    let result: [number, number];
    let resultSpan: [number, number];
    this.lazyInitTempRectStack();
    // 对触点坐标信息进行一定程度的偏移，所有的距离运算都基于网格左上角进行
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 71 : issue329.ts */
    const pixelX: number = cellAndSpan.getCellX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 72 : issue329.ts */
    const pixelY: number = cellAndSpan.getCellY();

    // bestLocation用于保存最终的计算结果
    let bestDistance: number = Number.MAX_VALUE;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 76 : issue329.ts */
    const bestRect: RectItem = new RectItem(- 1, - 1, - 1, - 1);
    const validRegions: Stack<RectItem> = new Stack<RectItem>();
    for (let i = 0; i < this.mRow - cellAndSpan.getSpanY() + 1; i++) {
      /* HPAudit: Do not access a const property in a heavy loop : hp-performance-no-const-prop-in-heavy-loop : 1 : 79 : issue329.ts */
      const T1 = i - 1;
      for (let j = 0; j < this.mColumn - cellAndSpan.getSpanX() + 1; j++) {
        let cellCoords: number[];
        cellCoords = this.cellToCenterPoint(j, i, cellAndSpan.getSpanX(), cellAndSpan.getSpanY());
        // 验证当前矩形不是以前任何候选矩形的子矩形,需要尽可能选择一个较大的矩形区域
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 83 : issue329.ts */
        const currentRect: RectItem = this.mTempRectStack.pop();
        if (! currentRect) {
          continue;
        }
        currentRect.set(j, i, j - 1, T1);
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 88 : issue329.ts */
        const isContained: boolean = this.isContained4FindNearestArea(validRegions, currentRect);
        validRegions.push(currentRect);
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 90 : issue329.ts */
        const distance: number = Math.hypot(cellCoords[0] - pixelX, cellCoords[1] - pixelY);

        // 最佳落点有两种情况。第一种是所有网格均可用，这种情况下网格中心点与被拖拽View中心点距离最短的网格坐标
        
        // 即可。第二种情况是不能使用占用的网格区域，在这种情况下，可能需要对spanX或者spanY进行缩放，找到一个
        
        // 可用的位置。此时距离最短不再是唯一条件，尽可能使可用尺寸接近spanX和spanY同样是需要考虑的条件
        if ((distance <= bestDistance && ! isContained) || currentRect.containsRect(bestRect)) {
          bestDistance = distance;
          result = [j, i];
          if (resultSpan != null) {
            resultSpan = [- 1, - 1];
          }
          bestRect.setRect(currentRect);
        }
      }
    }
    if (bestDistance === Number.MAX_VALUE) {
      result = [- 1, - 1];
    }
    this.recycleTempRects(validRegions);
    return result;
  }
  public isReArrangeSolutionSuccess(): boolean {
    return this.reArrangeSolutionSuccess;
  }
  public isNearReArrangeSolutionSuccess(): boolean {
    return this.reNearArrangeSolutionSuccess;
  }
  private lazyInitTempRectStack(): void {
    if (this.mTempRectStack.isEmpty()) {
      for (let i = 0; i < this.mRow * this.mColumn; i++) {
        this.mTempRectStack.push(new RectItem(0, 0, 0, 0));
      }
    }
  }
  private recycleTempRects(used: Stack<RectItem>): void {
    while (! used.isEmpty()) {
      this.mTempRectStack.push(used.pop());
    }
  }

  /**
   * 给定网格坐标，返回CellLayout中该网格中心实际的坐标位置
   *
   * @param cellX X轴坐标
   * @param cellY Y轴坐标
   * @param spanX X轴尺寸
   * @param spanY Y轴尺寸
   * @param result 计算结果
   */
  public cellToCenterPoint(cellX: number, cellY: number, spanX: number, spanY: number): number[] {
    return this.regionToCenterPoint(cellX, cellY, spanX, spanY);
  }

  /**
   * 给定网格坐标，返回CellLayout中该网格中心实际的坐标位置
   *
   * @param cellX X轴坐标
   * @param cellY Y轴坐标
   * @param spanX X轴尺寸
   * @param spanY Y轴尺寸
   * @param result 计算结果
   */
  public regionToCenterPoint(cellX: number, cellY: number, spanX: number, spanY: number): number[] {
    const horizontalStartPadding: number = this.mPaddingLeft;
    const verticalStartPadding: number = this.mPaddingTop;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 159 : issue329.ts */
    const cellWidth: number = this.mGridItemWidth;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 160 : issue329.ts */
    const cellHeight: number = this.mGridItemHeight;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 161 : issue329.ts */
    const fixedCellX: number = cellX;
    return [horizontalStartPadding + fixedCellX * cellWidth + (spanX * cellWidth - this.mColumnsGap) / 2, verticalStartPadding + cellY * cellHeight + (spanY * cellHeight - this.mRowsGap) / 2];
  }
  private isContained4FindNearestArea(validRegions: Stack<RectItem>, currentRect: RectItem): boolean {
    for (let rect of validRegions) {
      if (rect.containsRect(currentRect)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 计算当前重排列过程中的图标挤位方向
   *
   * @param itemInfo 拖拽View的位置信息
   * @param dragItemInfo 被拖拽的view
   * @param resultDirection 方向计算结果
   */
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 182 : issue329.ts */
  public getDirectionVectorForDrop(cellAndSpan: CellAndSpan, dragItemInfo: any, layoutInfo: any): number[] {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 183 : issue329.ts */
    const dragViewCenterX: number = cellAndSpan.getCellX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 184 : issue329.ts */
    const dragViewCenterY: number = cellAndSpan.getCellY();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 185 : issue329.ts */
    const spanX: number = cellAndSpan.getSpanX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 186 : issue329.ts */
    const spanY: number = cellAndSpan.getSpanY();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 187 : issue329.ts */
    const targetDestination: number[] = this.findNearestArea(cellAndSpan);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 188 : issue329.ts */
    const dragRect: RectItem = this.regionToRect(targetDestination[0], targetDestination[1], cellAndSpan.getSpanX(), cellAndSpan.getSpanY());
    dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
    // 计算落点处View的矩形区域位置及中心点
    let rect: RectItem = this.getViewsIntersectingRegion(targetDestination, dragItemInfo, layoutInfo, this.mIntersectingViews);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 192 : issue329.ts */
    const dropRegionSpanX: number = rect.width();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 193 : issue329.ts */
    const dropRegionSpanY: number = rect.height();
    rect = this.regionToRect(rect.left, rect.top, rect.width(), rect.height());
    // 计算x轴与y轴差值
    let deltaX: number = (rect.centerX() - dragViewCenterX) / spanX;
    let deltaY: number = (rect.centerY() - dragViewCenterY) / spanY;
    if (dropRegionSpanX === this.mRow || spanX === this.mRow) {
      deltaX = 0;
    }
    if (dropRegionSpanY === this.mColumn || spanY === this.mColumn) {
      deltaY = 0;
    }
    let resultDirection: number[];
    /* HPAudit: Do not use equality operators on floating point data : hp-specs-no-equality-ops-floating : 1 : 208 : issue329.ts */
    if (Math.abs(deltaX - 0) < Number.EPSILON && Math.abs(deltaY - 0) < Number.EPSILON) {
      
      // 若出现差值均为0的情况，给一个随机方向
      resultDirection = [1, 0];
    } else {
      resultDirection = this.computeDirectionVector(deltaX, deltaY);
    }
    this.mDirectionVector = resultDirection;
    log.showInfo(`resultDirection:${resultDirection[0]}, ${resultDirection[1]}`);
    return resultDirection;
  }
  private regionToRect(cellX: number, cellY: number, spanX: number, spanY: number): RectItem {
    const horizontalStartPadding: number = this.mPaddingLeft;
    const verticalStartPadding: number = this.mPaddingTop;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 222 : issue329.ts */
    const cellWidth: number = this.mGridItemWidth;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 223 : issue329.ts */
    const cellHeight: number = this.mGridItemHeight;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 224 : issue329.ts */
    const fixedCellX: number = cellX;
    const left: number = horizontalStartPadding + fixedCellX * cellWidth;
    const top: number = verticalStartPadding + cellY * cellHeight;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 227 : issue329.ts */
    const result: RectItem = new RectItem(left, top, left + (spanX * cellWidth), top + (spanY * cellHeight));
    return result;
  }
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 231 : issue329.ts */
  private getViewsIntersectingRegion(destination: number[], dragItemInfo: any, layoutInfo: any, intersectingViews: List<any>): RectItem {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 232 : issue329.ts */
    const cellX: number = destination[0];
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 233 : issue329.ts */
    const cellY: number = destination[1];
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 234 : issue329.ts */
    const spanX: number = dragItemInfo.area[0];
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 235 : issue329.ts */
    const spanY: number = dragItemInfo.area[1];
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 236 : issue329.ts */
    const boundingRect = new RectItem(cellX, cellY, cellX + spanX, cellY + spanY);
    intersectingViews.clear();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 239 : issue329.ts */
    const r0: RectItem = new RectItem(cellX, cellY, cellX + spanX, cellY + spanY);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 240 : issue329.ts */
    const r1: RectItem = new RectItem(0, 0, 0, 0);
    for (let i = 0; i < layoutInfo.length; i++) {
      if ((dragItemInfo.typeId === 0 && dragItemInfo.keyName !== layoutInfo[i].keyName) || ((dragItemInfo.cardId !== undefined && dragItemInfo.cardId !== layoutInfo[i].cardId)) || ((dragItemInfo.folderId !== undefined && dragItemInfo.folderId !== layoutInfo[i].folderId))) {
        r1.set(layoutInfo[i].column, layoutInfo[i].row, layoutInfo[i].column + layoutInfo[i].area[0], layoutInfo[i].row + layoutInfo[i].area[1]);
        if (RectItem.intersects(r0, r1)) {
          intersectingViews.add(layoutInfo[i]);
          boundingRect.union(r1);
        }
      }
    }
    return boundingRect;
  }

  /**
   * 根据两个坐标之间的相对位置，计算相对位置方向
   *
   * @param deltaX X轴位置差值
   * @param deltaY Y轴位置差值
   * @param result 计算结果
   */
  private computeDirectionVector(deltaX: number, deltaY: number): number[] {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 263 : issue329.ts */
    const angle: number = Math.atan(deltaY / deltaX);
    let result = [0, 0];
    if (Math.abs(Math.cos(angle)) > 0.5) {
      result[0] = deltaX > 0 ? 1 : - 1;
    }
    if (Math.abs(Math.sin(angle)) > 0.5) {
      result[1] = deltaY > 0 ? 1 : - 1;
    }
    return result;
  }
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 274 : issue329.ts */
  public getReorderSolution(cellAndSpan: CellAndSpan, dragItemInfo: any, layoutInfo: any): ItemConfiguration {
    
    // 通过某个方向的推动，或者图标位置替换找寻重排列方案
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 276 : issue329.ts */
    const swapSolution = this.findReorderSolution(cellAndSpan, this.mDirectionVector, dragItemInfo, layoutInfo, new ItemConfiguration(0, 0, 0, 0));
    return swapSolution;
  }
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 281 : issue329.ts */
  private findReorderSolution(cellAndSpan: CellAndSpan, direction: any, dragItemInfo, layoutInfo: any, solution: ItemConfiguration): any {
    
    // 在计算开始前，先保存未挤位前的图标位置信息与状态，当挤位失败或者取消拖拽到当前位置时，可以进行状态恢复
    this.copyCurrentStateToSolution(solution, layoutInfo);

    // 计算离拖拽View最近的网格位置
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 286 : issue329.ts */
    const result = this.findNearestArea(cellAndSpan);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 287 : issue329.ts */
    const spanX: number = cellAndSpan.getSpanX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 288 : issue329.ts */
    const spanY: number = cellAndSpan.getSpanY();

    // 基于最近网格位置进行图标重排列尝试
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 291 : issue329.ts */
    const isSuccess: boolean = this.rearrangementExists(new CellAndSpan(result[0], result[1], spanX, spanY), dragItemInfo, direction, layoutInfo, solution);
    this.reArrangeSolutionSuccess = isSuccess;
    if (isSuccess) {
      
      // 重排列方案生效，记录相关信息
      solution.setIsSolutionValid(true);
      solution.setCellX(result[0]);
      solution.setCellY(result[1]);
      solution.setSpanX(spanX);
      solution.setSpanY(spanY);
    }
    return solution;
  }
  public getNearReorderSolution(cellAndSpan: CellAndSpan, dragItemInfo: GridLayoutItemInfo, layoutInfo: List<GridLayoutItemInfo>, nearLayoutInfo: List<GridLayoutItemInfo>, solution: ItemConfiguration): ItemConfiguration {
    
    // 在计算开始前，先保存未挤位前的图标位置信息与状态，当挤位失败或者取消拖拽到当前位置时，可以进行状态恢复
    this.copyCurrentStateToSolution(solution, layoutInfo);
    solution.save();
    this.reNearArrangeSolutionSuccess = false;

    // 计算离拖拽View最近的网格位置
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 312 : issue329.ts */
    const result: number[] = this.findNearestArea(cellAndSpan);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 313 : issue329.ts */
    const spanX: number = cellAndSpan.getSpanX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 314 : issue329.ts */
    const spanY: number = cellAndSpan.getSpanY();
    // 基于最近网格位置进行图标重排列尝试
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 316 : issue329.ts */
    const isSuccess: boolean = this.nearRearRangementExits(new CellAndSpan(result[0], result[1], spanX, spanY), dragItemInfo, layoutInfo, nearLayoutInfo, solution);
    this.reArrangeSolutionSuccess = isSuccess;
    // 标记是否双屏挤位，双屏挤位，冲突序列改变了page
    this.reNearArrangeSolutionSuccess = isSuccess;
    if (isSuccess) {
      
      // 重排列方案生效，记录相关信息
      solution.setIsSolutionValid(true);
      solution.setCellX(result[0]);
      solution.setCellY(result[1]);
      solution.setSpanX(spanX);
      solution.setSpanY(spanY);
    }
    return solution;
  }
  public nearRearRangementExits(cellAndSpan: CellAndSpan, dragItemInfo: GridLayoutItemInfo, layoutInfo: List<GridLayoutItemInfo>, nearLayoutInfo: List<GridLayoutItemInfo>, solution: ItemConfiguration): boolean {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 334 : issue329.ts */
    const cellX: number = cellAndSpan.getCellX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 335 : issue329.ts */
    const cellY: number = cellAndSpan.getCellY();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 336 : issue329.ts */
    const spanX: number = cellAndSpan.getSpanX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 337 : issue329.ts */
    const spanY: number = cellAndSpan.getSpanY();
    if (cellX < 0 || cellY < 0) {
      return false;
    }
    if (nearLayoutInfo === undefined || nearLayoutInfo === null) {
      log.showError('nearRearRangementExits, nearLayoutInfo is invalid');
      return false;
    }
    this.mIntersectingViews.clear();
    this.mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 350 : issue329.ts */
    const r0 = new RectItem(cellX, cellY, cellX + spanX, cellY + spanY);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 351 : issue329.ts */
    const r1: RectItem = new RectItem(0, 0, 0, 0,);
    for (let i = 0; i < layoutInfo.length; i++) {
      if ((dragItemInfo?.typeId === CommonConstants.TYPE_APP && dragItemInfo?.keyName !== layoutInfo[i]?.keyName) || (dragItemInfo?.typeId === CommonConstants.TYPE_FOLDER && dragItemInfo?.folderId !== layoutInfo[i]?.folderId) || (dragItemInfo?.typeId === CommonConstants.TYPE_CARD && dragItemInfo?.cardId !== layoutInfo[i]?.cardId) || (dragItemInfo?.typeId === CommonConstants.TYPE_FORM_STACK && dragItemInfo?.formStackId !== layoutInfo[i]?.formStackId)) {
        r1.set(layoutInfo[i].column, layoutInfo[i].row, layoutInfo[i].column + layoutInfo[i].area[0], layoutInfo[i].row + layoutInfo[i].area[1]);
        if (RectItem.intersects(r0, r1)) {
          this.mIntersectingViews.add(layoutInfo[i]);
        }
      }
    }
    if (this.mIntersectingViews.length === 0) {
      return false;
    }
    solution.setIntersectingViews(this.mIntersectingViews);
    if (! this.mIntersectingViews || ! solution) {
      log.showError('nearRearRangementExits, direction or solution or mIntersectingViews is invalid');
      return false;
    }

    // 若从单一方向推动无效，则尝试将所有与落点冲突的View集合视为一个整体进行移动,不考虑方向
    if (this.addViewsToNearTempLocation(this.mIntersectingViews, nearLayoutInfo, r0, solution)) {
      return true;
    }

    // 若整体移动也失败，则将所有冲突的View单独进行找位，此时需要有一个共用的网格占用情况标记
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 379 : issue329.ts */
    const gridOccupyStatus: GridOccupyStatus = this.getGridOccupyStatus(nearLayoutInfo);

    // 在同一页面移动时需要取消拖拽组件的原占位
    for (let item of nearLayoutInfo) {
      if ((item?.typeId === CommonConstants.TYPE_APP && item?.keyName === dragItemInfo?.keyName) || (item?.typeId === CommonConstants.TYPE_FOLDER && item?.typeId === CommonConstants.TYPE_FOLDER && item?.folderId === dragItemInfo?.folderId) || (item?.typeId === CommonConstants.TYPE_CARD && item?.typeId === CommonConstants.TYPE_CARD && item?.cardId === dragItemInfo?.cardId) || (item?.typeId === CommonConstants.TYPE_FORM_STACK && item?.formStackId === dragItemInfo?.formStackId)) {
        gridOccupyStatus.markGridForCellAndSpan(new CellAndSpan(item.column, item.row, item.area[0], item.area[1]), GridOccupyStatusEnum.FREE);
        break;
      }
    }

    // 针对单个控件找位
    let isSolutionThreeSuccess: boolean = true;
    for (let view of this.mIntersectingViews) {
      if (! this.addViewToNearTempLocation(view, r0, gridOccupyStatus, solution)) {
        isSolutionThreeSuccess = false;
        break;
      }
    }
    return isSolutionThreeSuccess;
  }
  private addViewsToNearTempLocation(affectedViews: List<GridLayoutItemInfo>, nearLayout: List<GridLayoutItemInfo>, dragViewPotentialDrop: RectItem, currentState: ItemConfiguration): boolean {
    if (affectedViews.length === 0) {
      return true;
    }
    if (nearLayout === undefined || nearLayout === null) {
      log.showError('addViewsToNearTempLocation, nearLayout is invalid');
      return false;
    }

    // 获取当前页宫格网格占位情况
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 414 : issue329.ts */
    const pageOccupyStatus: GridOccupyStatus = this.getGridOccupyStatus(nearLayout);

    /*
     * 针对宫格上的原拖拽组件、受影响的控件，需要分别取消其原始占位
     * 然后针对拖拽组件做潜在落位占位，这部分位置不能被受影响的控件占用
     * 最后针对受到影响控件，可以在宫格上找一个最合适的区域
     * */
    /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 421 : issue329.ts */
    const dragItem: any = AppStorage.Get('dragItemInfo');
    for (let item of nearLayout) {
      if ((item?.typeId === CommonConstants.TYPE_APP && dragItem?.typeId === CommonConstants.TYPE_APP && item?.keyName === dragItem?.keyName) || (item?.typeId === CommonConstants.TYPE_FOLDER && dragItem?.typeId === CommonConstants.TYPE_FOLDER && item?.folderId === dragItem?.folderId) || (item?.typeId === CommonConstants.TYPE_CARD && dragItem?.typeId === CommonConstants.TYPE_CARD && item?.cardId === dragItem?.cardId) || (item?.typeId === CommonConstants.TYPE_FORM_STACK && dragItem?.typeId === CommonConstants.TYPE_FORM_STACK && item?.formStackId === dragItem?.formStackId)) {
        pageOccupyStatus.markGridForCellAndSpan(new CellAndSpan(item?.column, item?.row, item?.area[0], item?.area[1]), GridOccupyStatusEnum.FREE);
        break;
      }
    }

    // 将受影响的占位取消，并准备一个相对占位网格用于比较
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 434 : issue329.ts */
    const boundingRect: RectItem = new RectItem(0, 0, 0, 0);
    currentState.getBoundingRectForViews(affectedViews, boundingRect);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 436 : issue329.ts */
    const blockOccupied: GridOccupyStatus = new GridOccupyStatus(boundingRect.width(), boundingRect.height(), GridOccupyStatusEnum.FREE);
    for (let view of affectedViews) {
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 438 : issue329.ts */
      const cellAndSpan: CellAndSpan = currentState.getCellAndSpan(view);
      if (cellAndSpan === null) {
        log.showError('addViewsToNearTempLocation getCellAndSpan is null, view:%{public}s', view.moduleName);
        return false;
      }
      cellAndSpan.setCellX(cellAndSpan.getCellX() - boundingRect.left); // x坐标相对偏移
      cellAndSpan.setCellY(cellAndSpan.getCellY() - boundingRect.top); // y坐标相对偏移
      blockOccupied.markGridForCellAndSpan(cellAndSpan, GridOccupyStatusEnum.OCCUPIED); // 进行相对占位
      cellAndSpan.setCellX(cellAndSpan.getCellX() + boundingRect.left); // x坐标相对偏移还原
      cellAndSpan.setCellY(cellAndSpan.getCellY() + boundingRect.top); // y坐标相对偏移还原
    }

    // 接下来就可以比较当前页面宫格占位找一个可以放下小型网格的位置，先不考虑挤位方向实现
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 451 : issue329.ts */
    const tempLocation: number[] = [- 1, - 1];
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 452 : issue329.ts */
    const boundingCellAndSpan: CellAndSpan = new CellAndSpan(boundingRect.left, boundingRect.top, boundingRect.width(), boundingRect.height());
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 453 : issue329.ts */
    const isSwapSuccess = this.findAreaInNearLayout(boundingCellAndSpan, pageOccupyStatus, blockOccupied, tempLocation, true);
    if (! isSwapSuccess) {
      this.findAreaInNearLayout(boundingCellAndSpan, pageOccupyStatus, blockOccupied, tempLocation, false);
    }

    // 找到的位置有效时，则当前方案有效，前后偏移量即为每个app的偏移量
    let isSuccess: boolean = false;
    if (tempLocation[0] >= 0 && tempLocation[1] >= 0) {
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 461 : issue329.ts */
      const deltaX: number = tempLocation[0] - boundingRect.left;
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 462 : issue329.ts */
      const deltaY: number = tempLocation[1] - boundingRect.top;
      for (let view of affectedViews) {
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 464 : issue329.ts */
        const cellAndSpan: CellAndSpan = currentState.getCellAndSpan(view);
        if (cellAndSpan != null) {
          cellAndSpan.setCellX(cellAndSpan.getCellX() + deltaX);
          cellAndSpan.setCellY(cellAndSpan.getCellY() + deltaY);
        }
      }
      isSuccess = true;
    }
    return isSuccess;
  }
  private addViewToNearTempLocation(view: GridLayoutItemInfo, dragViewPotentialDrop: RectItem, gridOccupyStatus: GridOccupyStatus, currentState: ItemConfiguration): boolean {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 477 : issue329.ts */
    const cellAndSpan: CellAndSpan = currentState.getCellAndSpan(view);
    if (cellAndSpan == null) {
      return false;
    }
    let isSuccess: boolean = false;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 484 : issue329.ts */
    const tempLocation: number[] = [- 1, - 1];
    this.findAreaInNearLayout(cellAndSpan, gridOccupyStatus, null, tempLocation, false);
    if (tempLocation[0] >= 0 && tempLocation[1] >= 0) {
      isSuccess = true;
      cellAndSpan.setCellX(tempLocation[0]);
      cellAndSpan.setCellY(tempLocation[1]);
      gridOccupyStatus.markGridForCellAndSpan(cellAndSpan, GridOccupyStatusEnum.OCCUPIED);
    }
    return isSuccess;
  }
  public findAreaInNearLayout(cellAndSpan: CellAndSpan, pageOccupied: GridOccupyStatus, blockOccupied: GridOccupyStatus, result: number[], isSwap: boolean): boolean {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 498 : issue329.ts */
    const countX: number = pageOccupied.getSizeX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 499 : issue329.ts */
    const countY: number = pageOccupied.getSizeY();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 500 : issue329.ts */
    const spanX: number = cellAndSpan.getSpanX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 501 : issue329.ts */
    const spanY: number = cellAndSpan.getSpanY();
    /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 502 : issue329.ts */
    const dragItem: any = AppStorage.Get('dragItemInfo');
    let startX = 0;
    let startY = 0;
    if (isSwap) {
      startX = dragItem.column;
      startY = dragItem.row;
    }
    for (let y = startY; y < countY - (spanY - 1); ++y) {
      inner: for (let x = startX; x < countX - (spanX - 1); ++x) {
        for (let i = 0; i < spanX; ++i) {
          /* HPAudit: Do not access a const property in a heavy loop : hp-performance-no-const-prop-in-heavy-loop : 1 : 512 : issue329.ts */
          const T2 = x + i;
          for (let j = 0; j < spanY; ++j) {
            
            // 当前宫格上被占用的地方，不能被再次占用
            if (pageOccupied.isOccupied(T2, y + j) && (blockOccupied === null || blockOccupied.isOccupied(i, j))) {
              continue inner;
            }
          }
        }
        result[0] = x;
        result[1] = y;
        return true;
      }
    }
    result[0] = - 1;
    result[1] = - 1;
    return false;
  }
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 530 : issue329.ts */
  private copyCurrentStateToSolution(solution: ItemConfiguration, layoutInfo: any): void {
    for (let i = 0; i < layoutInfo.length; i++) {
      let cellSpan: CellAndSpan;
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 533 : issue329.ts */
      const cellX = layoutInfo[i].column;
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 534 : issue329.ts */
      const cellY = layoutInfo[i].row;
      cellSpan = new CellAndSpan(cellX, cellY, layoutInfo[i].area[0], layoutInfo[i].area[1]);
      solution.add(layoutInfo[i], cellSpan);
    }
  }
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 540 : issue329.ts */
  private rearrangementExists(cellAndSpan: CellAndSpan, dragItemInfo: any, direction: number[], layoutInfo: any, solution: ItemConfiguration): boolean {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 541 : issue329.ts */
    const cellX: number = cellAndSpan.getCellX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 542 : issue329.ts */
    const cellY: number = cellAndSpan.getCellY();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 543 : issue329.ts */
    const spanX: number = cellAndSpan.getSpanX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 544 : issue329.ts */
    const spanY: number = cellAndSpan.getSpanY();
    if (cellX < 0 || cellY < 0) {
      return false;
    }
    this.mIntersectingViews.clear();
    this.mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 553 : issue329.ts */
    const r0 = new RectItem(cellX, cellY, cellX + spanX, cellY + spanY);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 554 : issue329.ts */
    const r1: RectItem = new RectItem(0, 0, 0, 0,);
    for (let i = 0; i < layoutInfo.length; i++) {
      if ((dragItemInfo.typeId === CommonConstants.TYPE_APP && dragItemInfo.keyName !== layoutInfo[i].keyName) || (dragItemInfo.typeId === CommonConstants.TYPE_FOLDER && dragItemInfo.folderId !== layoutInfo[i].folderId) || (dragItemInfo.typeId === CommonConstants.TYPE_CARD && dragItemInfo.cardId !== layoutInfo[i].cardId) || (dragItemInfo.typeId === CommonConstants.TYPE_FORM_STACK && dragItemInfo.formStackId !== layoutInfo[i].formStackId)) {
        r1.set(layoutInfo[i].column, layoutInfo[i].row, layoutInfo[i].column + layoutInfo[i].area[0], layoutInfo[i].row + layoutInfo[i].area[1]);
        if (RectItem.intersects(r0, r1)) {
          this.mIntersectingViews.add(layoutInfo[i]);
        }
      }
    }
    if (this.mIntersectingViews.length === 0) {
      return false;
    }
    solution.setIntersectingViews(this.mIntersectingViews);
    if (! direction || ! this.mIntersectingViews || ! solution) {
      log.showError('rearrangementExists, direction or solution or mIntersectingViews is invalid');
      return false;
    }

    // 首先通过尝试从某个方向进行推动，从而达到重排列的目的
    if (this.attemptPushInDirection(r0, direction, dragItemInfo, this.mIntersectingViews, solution)) {
      return true;
    }

    // 若从单一方向推动无效，则尝试将所有与落点冲突的View集合视为一个整体进行移动
    if (this.addViewsToTempLocation(this.mIntersectingViews, layoutInfo, r0, direction, solution)) {
      return true;
    }

    // 若整体移动也失败，则将所有冲突的View单独进行找位，此时需要有一个共用的网格占用情况标记
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 588 : issue329.ts */
    const gridOccupyStatus: GridOccupyStatus = this.getGridOccupyStatus(layoutInfo);
    log.showInfo(`rearrangementExists solutionThree, gridOccupyStatus:${gridOccupyStatus.mStatus.toString()}`);

    // 在同一页面移动时需要取消拖拽组件的原占位
    for (let i = 0; i < layoutInfo.length; ++i) {
      if ((layoutInfo[i].typeId === CommonConstants.TYPE_APP && layoutInfo[i].keyName === dragItemInfo.keyName) || (layoutInfo[i].typeId === CommonConstants.TYPE_FOLDER && dragItemInfo.typeId === CommonConstants.TYPE_FOLDER && layoutInfo[i].folderId === dragItemInfo.folderId) || (layoutInfo[i].typeId === CommonConstants.TYPE_CARD && dragItemInfo.typeId === CommonConstants.TYPE_CARD && layoutInfo[i].cardId === dragItemInfo.cardId) || (layoutInfo[i].typeId === CommonConstants.TYPE_FORM_STACK && dragItemInfo.typeId === CommonConstants.TYPE_FORM_STACK && layoutInfo[i].formStackId === dragItemInfo.formStackId)) {
        gridOccupyStatus.markGridForCellAndSpan(new CellAndSpan(layoutInfo[i].column, layoutInfo[i].row, layoutInfo[i].area[0], layoutInfo[i].area[1]), GridOccupyStatusEnum.FREE);
        break;
      }
    }

    // 针对潜在落位进行占位
    gridOccupyStatus.markGridForRect(r0, GridOccupyStatusEnum.OCCUPIED);

    // 针对单个控件找位
    let isSolutionThreeSuccess: boolean = true;
    for (let view of this.mIntersectingViews) {
      if (! this.addViewToTempLocation(view, r0, direction, gridOccupyStatus, solution)) {
        isSolutionThreeSuccess = false;
        break;
      }
    }
    return isSolutionThreeSuccess;
  }

  /**
   * 尝试通过不同方向的推动找寻重排列方案
   *
   * @param occupied 拖拽落点占用的区域
   * @param direction 挤位方向
   * @param dragItemInfo 被拖拽的View
   * @param intersectingViews 与拖拽落点产生的冲突的View集合
   * @param solution 重排列解决方案
   * @return 是否有可用解决方案
   */
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 631 : issue329.ts */
  private attemptPushInDirection(occupied: RectItem, direction: number[], dragItemInfo: any, intersectingViews: List<any>, solution: ItemConfiguration): boolean {
    if ((Math.abs(direction[0]) + Math.abs(direction[1])) > 1) {
      
      // 如果X和Y两个方向都不为0，分别从X和Y单独进行尝试
      // 第一次尝试
      if (this.checkPushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
        return true;
      }
      // 第二次反向尝试
      if (this.checkPushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
        return true;
      }
    } else {
      
      // 如果X和Y仅有一个方向不为0，优先从该方向进行尝试
      if (this.pushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
        return true;
      }
      // 进行反向尝试
      direction[0] = - direction[0];
      direction[1] = - direction[1];
      if (this.pushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
        return true;
      }
      // 如果上面的方向无法获取到重排列方案，尝试从另外一个方向获取交换方向（先恢复，再尝试）
      let temp: number = - direction[1];
      direction[1] = - direction[0];
      direction[0] = temp;
      if (this.pushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
        return true;
      }

      // 进行反向尝试
      direction[0] = - direction[0];
      direction[1] = - direction[1];
      if (this.pushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
        return true;
      }
      // 反向恢复并恢复到原始方向
      temp = - direction[1];
      direction[1] = - direction[0];
      direction[0] = temp;
    }
    return false;
  }
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 676 : issue329.ts */
  private checkPushViewsToTempLocation(intersectingViews: List<any>, occupied: RectItem, direction: number[], dragItemInfo: any, solution: ItemConfiguration): boolean {
    
    // 首先从X方向进行尝试
    let temp = direction[1];
    direction[1] = 0;
    if (this.pushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
      return true;
    }
    direction[1] = temp;

    // 从Y方向进行尝试
    temp = direction[0];
    direction[0] = 0;
    if (this.pushViewsToTempLocation(intersectingViews, occupied, direction, dragItemInfo, solution)) {
      return true;
    }
    // 反向
    direction[0] = - temp;
    direction[1] = - direction[1];
    return false;
  }

  /**
   *
   * @param intersectingViews 与拖拽落点产生的冲突的View集合
   * @param occupied 拖拽落点占用的区域
   * @param direction 挤位方向
   * @param dragItemInfo 被拖拽的View
   * @param solution 解决方案
   */
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 706 : issue329.ts */
  private pushViewsToTempLocation(intersectingViews: List<any>, occupied: RectItem, direction: number[], dragItemInfo: any, solution: ItemConfiguration): boolean {
    
    // cluster中保存了需要改变位置的View集合，后面简称为集群
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 709 : issue329.ts */
    const cluster: ViewCluster = new ViewCluster(intersectingViews, solution, this.mRow, this.mColumn);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 710 : issue329.ts */
    const clusterRect: RectItem = cluster.getBoundingRect();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 712 : issue329.ts */
    const pair = this.calculatePushDirectionAndDistance(direction, clusterRect, occupied);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 713 : issue329.ts */
    const whichEdge: number = pair[0];
    let pushDistance: number = pair[1];
    if (pushDistance <= 0) {
      return false;
    }

    // 保存当前CellLayout上所有View的位置和尺寸信息。若挤位失败，可用过保存状态进行恢复。
    solution.save();

    // 对当前CellLayout上所有的View进行排序。排序的优先级是按照推动方向以及View的网格边缘综合考虑。
    
    // 例如，当推动方向是从左往右时，则左边缘与被推动区域更加靠近的View优先级更高。
    
    // 这么做的目的主要在于优化算法的时间复杂度
    cluster.sortConfigurationForEdgePush(whichEdge);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 727 : issue329.ts */
    /* HPAudit: Name literal constants in uppercase, words separated by underscores : hp-specs-enum-ucase-uscore : 1 : 727 : issue329.ts */
    const IS_FAIL: boolean = false;
    if (direction[0] === 1 || direction[1] === 1) {
      solution.getSortedViews().sort( (left, right) => {
        return left.row * this.mColumn + left.column - (right.row * this.mColumn + right.column);
      });
    } else {
      solution.getSortedViews().sort( (left, right) => {
        return right.row * this.mColumn + right.column - (left.row * this.mColumn + left.column);
      });
    }
    while (pushDistance > 0 && ! IS_FAIL) {
      for (let view of solution.getSortedViews()) {
        if (view == null) {
          continue;
        }
        if (cluster.isContainView(view) || ((view.typeId === CommonConstants.TYPE_APP && view.keyName === dragItemInfo.keyName) || (view.typeId === CommonConstants.TYPE_CARD && view.cardId === dragItemInfo.cardId) || (view.typeId === CommonConstants.TYPE_FOLDER && view.folderId === dragItemInfo.folderId) || (view.typeId === CommonConstants.TYPE_FORM_STACK && view.formStackId === dragItemInfo.formStackId))) {
          continue;
        }
        // 若当前View还不在集群中，但是边缘与集群产生碰撞，则将其加入集群中
        if (! cluster.isViewTouchingEdge(view, whichEdge)) {
          continue;
        }

        // 将符合推动要求的View加入集群，并释放其原有位置
        cluster.addView(view);
      }
      // 需要推动的距离决定循环次数
      pushDistance--;
      // 将集群中所有的View根据推动方向更新位置
      cluster.shift(whichEdge);
    }
    let isFoundSolution: boolean = false;
    // 对算法结果进行校验，若最终坐标范围有效，则解决方案可行
    if (! IS_FAIL && this.isClusterRectValid(cluster.getBoundingRect())) {
      isFoundSolution = true;
      solution.setIntersectingViews(cluster.getClusterViews());
    } else {
      solution.restore();
    }
    log.showInfo(`pushViewsToTempLocation isFoundSolution:${isFoundSolution},cluster.getBoundingRect():${cluster.getBoundingRect().toString()}`);
    return isFoundSolution;
  }
  private isClusterRectValid(clusterRect: RectItem): boolean {
    return clusterRect.left >= 0 && clusterRect.right <= this.mColumn && clusterRect.top >= 0 && clusterRect.bottom <= this.mRow;
  }

  /**
   *
   * @param direction
   * @param clusterRect
   * @param potentialDropRect
   */
  private calculatePushDirectionAndDistance(direction: number[], clusterRect: RectItem, potentialDropRect: RectItem): number[] {
    let whichEdge: number;
    let pushDistance: number;

    // 计算将引导推动的边缘，以及需要推动的距离。其中边缘根据推动方向决定
    if (direction[0] < 0) {
      whichEdge = 1;
      pushDistance = clusterRect.right - potentialDropRect.left;
    } else if (direction[0] > 0) {
      whichEdge = 1 << 2;
      pushDistance = potentialDropRect.right - clusterRect.left;
    } else if (direction[1] < 0) {
      whichEdge = 1 << 1;
      pushDistance = clusterRect.bottom - potentialDropRect.top;
    } else {
      whichEdge = 1 << 3;
      pushDistance = potentialDropRect.bottom - clusterRect.top;
    }
    return [whichEdge, pushDistance];
  }

  /**
   * 为多个被挤位影响到的view找一块可用区域，找位后的结果需要保证相对位置不变
   *
   * @param affectedViews         被影响的view集合
   * @param dragViewPotentialDrop 拖拽View当前潜在落位
   * @param direction             拖拽View挤位方向
   * @param currentState          当前CellLayout所有View的位置与尺寸信息
   * @return true即找位成功，否则失败
   * @date 2023-02-20
   */
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 819 : issue329.ts */
  private addViewsToTempLocation(affectedViews: List<GridLayoutItemInfo>, layout: any, dragViewPotentialDrop: RectItem, direction: number[], currentState: ItemConfiguration): boolean {
    log.showInfo(`addViewsToTempLocation start, affectedViews length:${affectedViews.length}, dragViewPotentialDrop:${dragViewPotentialDrop.toString()}, direction:${direction.toString()}`);
    if (affectedViews.length === 0) {
      return true;
    }

    // 获取当前页宫格网格占位情况
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 828 : issue329.ts */
    const pageOccupyStatus: GridOccupyStatus = this.getGridOccupyStatus(layout);

    /*
     * 针对宫格上的原拖拽组件、受影响的控件，需要分别取消其原始占位
     * 然后针对拖拽组件做潜在落位占位，这部分位置不能被受影响的控件占用
     * 最后针对受到影响控件，可以在宫格上找一个最合适的区域
     * */
    
    // 需要取消拖拽组件的原占位
    /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 836 : issue329.ts */
    const dragItem: any = AppStorage.Get('dragItemInfo');
    const layoutInfo = currentState.getSortedViews();
    for (let item of layoutInfo) {
      if ((item.typeId === CommonConstants.TYPE_APP && dragItem.typeId === CommonConstants.TYPE_APP && item.keyName === dragItem.keyName) || (item.typeId === CommonConstants.TYPE_FOLDER && dragItem.typeId === CommonConstants.TYPE_FOLDER && item.folderId === dragItem.folderId) || (item.typeId === CommonConstants.TYPE_CARD && dragItem.typeId === CommonConstants.TYPE_CARD && item.cardId === dragItem.cardId) || (item.typeId === CommonConstants.TYPE_FORM_STACK && dragItem.typeId === CommonConstants.TYPE_FORM_STACK && item.formStackId === dragItem.formStackId)) {
        pageOccupyStatus.markGridForCellAndSpan(new CellAndSpan(item.column, item.row, item.area[0], item.area[1]), GridOccupyStatusEnum.FREE);
        break;
      }
    }

    // 将受影响的占位取消，并准备一个相对占位网格用于比较
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 850 : issue329.ts */
    const boundingRect: RectItem = new RectItem(0, 0, 0, 0);
    currentState.getBoundingRectForViews(affectedViews, boundingRect);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 852 : issue329.ts */
    const blockOccupied: GridOccupyStatus = new GridOccupyStatus(boundingRect.width(), boundingRect.height(), GridOccupyStatusEnum.FREE);
    for (let view of affectedViews) {
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 854 : issue329.ts */
      const cellAndSpan: CellAndSpan = currentState.getCellAndSpan(view);
      if (cellAndSpan === null) {
        log.showError('addViewsToTempLocation getCellAndSpan is null, view:%{public}s', view.moduleName);
        return false;
      }
      pageOccupyStatus.markGridForCellAndSpan(cellAndSpan, GridOccupyStatusEnum.FREE); // 取消页面占位
      cellAndSpan.setCellX(cellAndSpan.getCellX() - boundingRect.left); // x坐标相对偏移
      cellAndSpan.setCellY(cellAndSpan.getCellY() - boundingRect.top); // y坐标相对偏移
      blockOccupied.markGridForCellAndSpan(cellAndSpan, GridOccupyStatusEnum.OCCUPIED); // 进行相对占位
      cellAndSpan.setCellX(cellAndSpan.getCellX() + boundingRect.left); // x坐标相对偏移还原
      cellAndSpan.setCellY(cellAndSpan.getCellY() + boundingRect.top); // y坐标相对偏移还原
    }

    // 针对drop项的潜在落位需要在当前宫格页面占位
    pageOccupyStatus.markGridForRect(dragViewPotentialDrop, GridOccupyStatusEnum.OCCUPIED);

    // 接下来就可以比较当前页面宫格占位找一个可以放下小型网格的位置，先不考虑挤位方向实现
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 871 : issue329.ts */
    const tempLocation: number[] = [- 1, - 1];
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 872 : issue329.ts */
    const boundingCellAndSpan: CellAndSpan = new CellAndSpan(boundingRect.left, boundingRect.top, boundingRect.width(), boundingRect.height());
    this.findNearestAreaTwo(boundingCellAndSpan, direction, pageOccupyStatus, blockOccupied, tempLocation);

    // 找到的位置有效时，则当前方案有效，前后偏移量即为每个app的偏移量
    let isSuccess: boolean = false;
    if (tempLocation[0] >= 0 && tempLocation[1] >= 0) {
      
      // 被挤位app进行移位
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 879 : issue329.ts */
      const deltaX: number = tempLocation[0] - boundingRect.left;
      /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 880 : issue329.ts */
      const deltaY: number = tempLocation[1] - boundingRect.top;
      for (let view of affectedViews) {
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 883 : issue329.ts */
        const cellAndSpan: CellAndSpan = currentState.getCellAndSpan(view);
        if (cellAndSpan != null) {
          cellAndSpan.setCellX(cellAndSpan.getCellX() + deltaX);
          cellAndSpan.setCellY(cellAndSpan.getCellY() + deltaY);
        }
      }
      isSuccess = true;
    }
    return isSuccess;
  }

  /**
   * 获取grid占位状态
   * @param layoutInfo
   * @date 2023-02-20
   */
  /* HPAudit: Avoid using 'any' : hp-specs-no-any : 1 : 900 : issue329.ts */
  public getGridOccupyStatus(layoutInfo: any): GridOccupyStatus {
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 901 : issue329.ts */
    const gridOccupyStatus: GridOccupyStatus = new GridOccupyStatus(this.mColumn, this.mRow, GridOccupyStatusEnum.FREE);
    for (let i = 0; i < layoutInfo?.length; ++i) {
      gridOccupyStatus.markGridForRect(new RectItem(layoutInfo[i].column, layoutInfo[i].row, layoutInfo[i].column + layoutInfo[i].area[0], layoutInfo[i].row + layoutInfo[i].area[1]), GridOccupyStatusEnum.OCCUPIED);
    }
    return gridOccupyStatus;
  }

  /**
   * 在当前CellLayout上，为给定的位置信息找一个最近的可用网格。此方法比较的是网格坐标之间的距离，而不是像素距离，需要考虑占位信息
   *
   * @param cellAndSpan   当前被挤位的view的位置信息
   * @param direction     移动方向
   * @param occupied      当前页的网格占用状态
   * @param blockOccupied 表示指定的矩形区域中网格占用状态。当尝试移动一整组View时使用
   * @param result        保存计算结果
   * @return 最近的可用网格
   * @date 2023-02-20
   */
  public findNearestAreaTwo(cellAndSpan: CellAndSpan, direction: number[], pageOccupied: GridOccupyStatus, blockOccupied: GridOccupyStatus, result: number[]): number[] {
    log.showInfo(`findNearestAreaTwo start cellAndSpan:${cellAndSpan.toString()}, direction:${direction.toString()}`);
    let bestLocation: number[] = result !== null ? result : [- 1, - 1];
    let bestDistance: number = Number.MAX_SAFE_INTEGER;
    let bestDirectionScore: number = Number.MIN_SAFE_INTEGER;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 930 : issue329.ts */
    const countX: number = pageOccupied.getSizeX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 931 : issue329.ts */
    const countY: number = pageOccupied.getSizeY();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 932 : issue329.ts */
    const cellX: number = cellAndSpan.getCellX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 933 : issue329.ts */
    const cellY: number = cellAndSpan.getCellY();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 934 : issue329.ts */
    const spanX: number = cellAndSpan.getSpanX();
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 935 : issue329.ts */
    const spanY: number = cellAndSpan.getSpanY();
    for (let y = 0; y < countY - (spanY - 1); ++y) {
      inner: for (let x = 0; x < countX - (spanX - 1); ++x) {
        for (let i = 0; i < spanX; ++i) {
          /* HPAudit: Do not access a const property in a heavy loop : hp-performance-no-const-prop-in-heavy-loop : 1 : 940 : issue329.ts */
          const T3 = x + i;
          for (let j = 0; j < spanY; ++j) {
            
            // 当前宫格上被占用的地方，不能被再次占用
            if (pageOccupied.isOccupied(T3, y + j) && (blockOccupied == null || blockOccupied.isOccupied(i, j))) {
              continue inner;
            }
          }
        }

        // 分别计算每种可用情况的距离和得分，取最优的方案
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 949 : issue329.ts */
        const distance: number = Math.hypot(x - cellX, y - cellY);
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 950 : issue329.ts */
        const curDirection: number[] = this.computeDirectionVector(x - cellX, y - cellY);
        /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 951 : issue329.ts */
        const curDirectionScore: number = direction[0] * curDirection[0] + direction[1] * curDirection[1];
        if (distance < bestDistance || (Math.abs(distance - bestDistance) < 0.000001 && curDirectionScore > bestDirectionScore)) {
          bestDistance = distance;
          bestDirectionScore = curDirectionScore;
          bestLocation[0] = x;
          bestLocation[1] = y;
        }
      }
    }
    if (bestDistance === Number.MAX_SAFE_INTEGER) {
      bestLocation[0] = Number.MIN_SAFE_INTEGER;
      bestLocation[1] = Number.MIN_SAFE_INTEGER;
    }
    return bestLocation;
  }

  /**
   * 为单个View寻找可用区域
   *
   * @param view                  目标View
   * @param dragViewPotentialDrop 拖拽View落点占用区域
   * @param direction             找位方向
   * @param currentState          当前CellLayout所有View的位置与尺寸信息
   * @return 是否找位成功
   * @date 2023-02-20
   */
  private addViewToTempLocation(view: GridLayoutItemInfo, dragViewPotentialDrop: RectItem, direction: number[], gridOccupyStatus: GridOccupyStatus, currentState: ItemConfiguration): boolean {
    log.showInfo(`addViewToTempLocation start, view:${view.keyName}, dragViewPotentialDrop:${dragViewPotentialDrop.toString()}, direction:${direction.toString()}`);
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 982 : issue329.ts */
    const cellAndSpan: CellAndSpan = currentState.getCellAndSpan(view);
    if (cellAndSpan == null) {
      return false;
    }
    let isSuccess: boolean = false;
    /* HPAudit: Declare unchanged variables as const : hp-performance-unchanged-const-vars : 1 : 989 : issue329.ts */
    const tempLocation: number[] = [- 1, - 1];
    this.findNearestAreaTwo(cellAndSpan, direction, gridOccupyStatus, null, tempLocation);
    if (tempLocation[0] >= 0 && tempLocation[1] >= 0) {
      isSuccess = true;
      cellAndSpan.setCellX(tempLocation[0]);
      cellAndSpan.setCellY(tempLocation[1]);
      gridOccupyStatus.markGridForCellAndSpan(cellAndSpan, GridOccupyStatusEnum.OCCUPIED);
    }
    return isSuccess;
  }
}
